package com.xiaolin.oa.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.xiaolin.oa.constants.ApprovalTypeConstant;
import com.xiaolin.oa.constants.FlagConstant;
import com.xiaolin.oa.constants.ProcessFlowStatusConstants;
import com.xiaolin.oa.constants.ProcessNodeStatusConstant;
import com.xiaolin.oa.exception.ServiceException;
import com.xiaolin.oa.mapper.CommonProcessApprovalRecordMapper;
import com.xiaolin.oa.mapper.CommonProcessApproverMapper;
import com.xiaolin.oa.mapper.CommonProcessMapper;
import com.xiaolin.oa.mapper.CommonProcessNodeMapper;
import com.xiaolin.oa.model.dto.*;
import com.xiaolin.oa.model.entity.*;
import com.xiaolin.oa.service.*;
import com.xiaolin.oa.utils.ContextUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 审批流程表 服务实现类
 * </p>
 *
 * @author xiaolin
 * @since 2024-08-22
 */
@Service
public class CommonProcessServiceImpl extends ServiceImpl<CommonProcessMapper, CommonProcess> implements CommonProcessService {
    @Autowired
    private CommonProcessExecutorService processExecutorService;

    @Autowired
    private CommonProcessFlowService processFlowService;

    @Autowired
    private SysUserService userService;

    @Autowired
    private SysOrganizationService organizationService;

    @Autowired
    private CommonProcessMapper processMapper;

    @Autowired
    private CommonProcessNodeMapper processNodeMapper;

    @Autowired
    private CommonProcessApproverMapper processApproverMapper;

    @Autowired
    private CommonProcessApprovalRecordMapper processApprovalRecordMapper;

    @Autowired
    private ContextUtil contextUtil;

    @Override
    public List<CommonProcess> getProcessList(CommonProcess process) {
        return processMapper.getProcessList(process);
    }

    @Override
    public PageInfo<CommonProcess> getProcessPage(Integer page, Integer pageSize, CommonProcess process) {
        PageHelper.startPage(page, pageSize);
        List<CommonProcess> processList = processMapper.getProcessList(process);
        return new PageInfo<>(processList);
    }

    /**
     * 校验审批流程唯一性
     *
     * @param process
     * @return
     */
    @Override
    public void checkUnique(CommonProcess process) {
        long count = processMapper.selectCount(new LambdaQueryWrapper<CommonProcess>()
                .and(qw -> qw.eq(CommonProcess::getProcessName, process.getProcessName()).or().eq(CommonProcess::getProcessCode, process.getProcessCode()))
                .ne(Objects.nonNull(process.getId()), CommonProcess::getId, process.getId()));
        if (count > 0) {
            ServiceException.error("审批流程编码或审批流程名重复了");
        }
    }

    @Override
    public boolean addProcess(CommonProcess process) {
        CurrentUser user = contextUtil.getCurrentUser();
        // 校验审批流程唯一性
        checkUnique(process);
        process.setCreator(user.getId());
        process.setUpdator(user.getId());
        processMapper.insert(process);
        return true;
    }

    @Override
    public CommonProcess getProcess(Integer id) {
        return processMapper.selectById(id);
    }

    @Override
    public boolean updateProcess(CommonProcess process) {
        // 校验审批流程唯一性
        checkUnique(process);
        processMapper.updateById(process);
        return true;
    }

    @Override
    public boolean status(Integer id, String status) {
        CurrentUser user = contextUtil.getCurrentUser();
        processMapper.update(new CommonProcess(), new UpdateWrapper<CommonProcess>().lambda()
                .set(CommonProcess::getAvailable, status)
                .set(CommonProcess::getUpdator, user.getId())
                .eq(CommonProcess::getId, id));
        return true;
    }

    @Override
    public boolean configOrgForProcess(Integer processId, List<Integer> orgIds) {
        // 校验流程绑定的组织架构是否已分配审批人
        // 获取流程审批人所属组织架构
        List<Integer> configOrgIds = processNodeMapper.getProcessApproverOrgs(processId);
        // 禁止解绑的组织架构
        List<Integer> disableBindingOrgIds = configOrgIds.stream().filter(orgId -> !orgIds.contains(orgId)).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(disableBindingOrgIds)) {
            List<String> orgNames = organizationService.list(new LambdaQueryWrapper<SysOrganization>().in(SysOrganization::getId, disableBindingOrgIds))
                    .stream().map(SysOrganization::getOrgName).collect(Collectors.toList());
            ServiceException.error(orgNames + " 已分配审批人，请先解绑审批人");
        }
        CurrentUser user = contextUtil.getCurrentUser();
        // 删除流程已绑定的执行人组织架构
        processExecutorService.update(new UpdateWrapper<CommonProcessExecutor>().lambda()
                .set(CommonProcessExecutor::getFlag, FlagConstant.DELETED)
                .set(CommonProcessExecutor::getUpdator, user.getId())
                .eq(CommonProcessExecutor::getProcessId, processId));
        // 新增要绑定的执行人组织架构
        List<CommonProcessExecutor> processExecutors = orgIds.stream().map(orgId -> {
            CommonProcessExecutor processExecutor = new CommonProcessExecutor();
            processExecutor.setProcessId(processId);
            processExecutor.setOrgId(orgId);
            processExecutor.setCreator(user.getId());
            processExecutor.setUpdator(user.getId());
            return processExecutor;
        }).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(processExecutors)) {
            processExecutorService.saveBatch(processExecutors);
        }
        return true;
    }

    @Override
    public List<Integer> getBoundOrgs(Integer processId) {
        List<CommonProcessExecutor> processExecutors = processExecutorService.list(new LambdaQueryWrapper<CommonProcessExecutor>().eq(CommonProcessExecutor::getProcessId, processId));
        return processExecutors.stream().map(CommonProcessExecutor::getOrgId).collect(Collectors.toList());
    }

    @Override
    public List<SysUserView> getExecutorOrgUserList(Integer processId) {
        List<SysUserView> userList = new ArrayList<>();
        // 获取流程下的执行人组织架构
        List<Integer> orgIds = processExecutorService
                .list(new LambdaQueryWrapper<CommonProcessExecutor>().eq(CommonProcessExecutor::getProcessId, processId))
                .stream().map(CommonProcessExecutor::getOrgId).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(orgIds)) {
            SysUser user = new SysUser();
            user.setOrgIds(orgIds);
            return userService.getUserList(user);
        }
        return userList;
    }

    @Override
    public ProcessNodeDetail getFirstProcessNode(String processCode) {
        ProcessNodeDetail nodeDetail = processNodeMapper.getFirstProcessNode(processCode);
        if (Objects.isNull(nodeDetail)) {
            ServiceException.error("流程或流程节点不存在，请配置");
        }
        List<NodeApprover> nodeApprovers = processApproverMapper.getProcessNodeApprovers(nodeDetail.getProcessNodeId());
        if (CollectionUtils.isEmpty(nodeApprovers)) {
            ServiceException.error("流程节点未绑定审批人，请配置");
        }
        nodeDetail.setApprovers(nodeApprovers);
        return nodeDetail;
    }

    @Override
    public ProcessNodeDetail getNextProcessNode(String serialNumber, Integer processFlowId) {
        ProcessNodeDetail nodeDetail = processNodeMapper.getNextProcessNode(serialNumber, processFlowId);
        if (Objects.isNull(nodeDetail)) {
            return null;
        }
        List<NodeApprover> nodeApprovers = processApproverMapper.getProcessNodeApproversByFlow(nodeDetail.getProcessNodeId());
        if (CollectionUtils.isEmpty(nodeApprovers)) {
            ServiceException.error("流程节点未绑定审批人，请配置");
        }
        nodeDetail.setApprovers(nodeApprovers);
        return nodeDetail;
    }

    @Transactional
    @Override
    public void startProcess(StartProcessDto startProcessDto) {
        CurrentUser user = contextUtil.getCurrentUser();
        List<ProcessNodeDto> processNodeDtos = processNodeMapper.getProcessNodeListByProcessCode(startProcessDto.getProcessCode());
        if (CollectionUtils.isEmpty(processNodeDtos)) {
            ServiceException.error("流程或流程节点不存在，请配置");
        }
        // 创建流程流转信息记录
        String serialNumber = startProcessDto.getSerialNumber();
        List<CommonProcessFlow> processFlows = processNodeDtos.stream().map(node -> {
            CommonProcessFlow processFlow = new CommonProcessFlow();
            processFlow.setSerialNumber(serialNumber);
            processFlow.setProcessCode(node.getProcessCode());
            processFlow.setProcessName(node.getProcessName());
            processFlow.setProcessNodeId(node.getId());
            processFlow.setProcessNodeCode(node.getProcessNodeCode());
            processFlow.setProcessNodeName(node.getProcessNodeName());
            processFlow.setApprovalType(node.getApprovalType());
            processFlow.setSort(node.getSort());
            processFlow.setCreator(user.getId());
            processFlow.setUpdator(user.getId());
            return processFlow;
        }).collect(Collectors.toList());
        processFlowService.saveBatch(processFlows);
        // 创建流程审批任务
        CommonProcessApprovalRecord processApprovalRecord = new CommonProcessApprovalRecord();
        processApprovalRecord.setSerialNumber(serialNumber);
        processApprovalRecord.setProcessFlowId(processFlows.get(0).getId());
        processApprovalRecord.setAppointApprover(startProcessDto.getAppointApprover());
        processApprovalRecord.setCreator(user.getId());
        processApprovalRecord.setUpdator(user.getId());
        processApprovalRecordMapper.insert(processApprovalRecord);
    }

    @Transactional
    @Override
    public String handleProcess(HandleProcessDto handleProcessDto) {
        String serialNumber = handleProcessDto.getSerialNumber();
        Integer processFlowId = handleProcessDto.getProcessFlowId();
        CommonProcessApprovalRecord curApprovalRecord = processApprovalRecordMapper.getCurApprovalRecord(serialNumber, processFlowId);
        if (Objects.isNull(curApprovalRecord)) {
            ServiceException.error("当前流程节点已被审批，请刷新");
        }
        String processStatus = "";
        switch (handleProcessDto.getApprovalType()) {
            case ApprovalTypeConstant.ONE_SIGN:
                // 单人审批
                processStatus = oneSignApprove(handleProcessDto, curApprovalRecord);
                break;
            case ApprovalTypeConstant.OR_SIGN:
                // 多人或签
                processStatus = orSignApprove(handleProcessDto, curApprovalRecord);
                break;
            case ApprovalTypeConstant.AND_SIGN:
                // 多人会签
                processStatus = andSignApprove(handleProcessDto, curApprovalRecord);
                break;
            default:
                break;
        }
        if (StringUtils.isBlank(processStatus)) {
            ServiceException.error("");
        }
        return processStatus;
    }

    /**
     * 单人审批
     *
     * @param handleProcessDto
     * @param curApprovalRecord
     * @return
     */
    private String oneSignApprove(HandleProcessDto handleProcessDto, CommonProcessApprovalRecord curApprovalRecord) {
        CurrentUser user = contextUtil.getCurrentUser();
        String serialNumber = handleProcessDto.getSerialNumber();
        Integer approver = handleProcessDto.getApprover();
        if (!curApprovalRecord.getAppointApprover().equals(String.valueOf(approver))) {
            ServiceException.error("当前审批人不合法");
        }
        // 处理节点
        curApprovalRecord.setNodeStatus(ProcessNodeStatusConstant.END);
        curApprovalRecord.setApprover(approver);
        curApprovalRecord.setOperationType(handleProcessDto.getOperationType());
        curApprovalRecord.setApprovalOpinion(handleProcessDto.getApprovalOpinion());
        curApprovalRecord.setApprovalRemark(handleProcessDto.getApprovalRemark());
        curApprovalRecord.setUpdator(user.getId());
        processApprovalRecordMapper.updateById(curApprovalRecord);
        if (handleProcessDto.isEnd()) {
            // 手动完结
            return ProcessFlowStatusConstants.MANUAL_END;
        }
        NextProcessNode nextProcessNode = handleProcessDto.getNextProcessNode();
        if (Objects.isNull(nextProcessNode)) {
            // 自动完结
            return ProcessFlowStatusConstants.AUTO_END;
        }
        CommonProcessApprovalRecord nextApprovalRecord = new CommonProcessApprovalRecord();
        nextApprovalRecord.setSerialNumber(serialNumber);
        nextApprovalRecord.setProcessFlowId(nextProcessNode.getProcessFlowId());
        nextApprovalRecord.setAppointApprover(nextProcessNode.getAppointApprover());
        nextApprovalRecord.setCreator(user.getId());
        nextApprovalRecord.setUpdator(user.getId());
        processApprovalRecordMapper.insert(nextApprovalRecord);
        return ProcessFlowStatusConstants.BEING;
    }

    /**
     * 多人或签
     *
     * @param handleProcessDto
     * @param curApprovalRecord
     * @return
     */
    private String orSignApprove(HandleProcessDto handleProcessDto, CommonProcessApprovalRecord curApprovalRecord) {
        CurrentUser user = contextUtil.getCurrentUser();
        String serialNumber = handleProcessDto.getSerialNumber();
        Integer processFlowId = handleProcessDto.getProcessFlowId();
        Integer approver = handleProcessDto.getApprover();
        List<String> appointApprovers = Arrays.asList(curApprovalRecord.getAppointApprover().split(","));
        if (!appointApprovers.contains(String.valueOf(approver))) {
            ServiceException.error("当前审批人不合法");
        }
        // 处理节点
        curApprovalRecord.setNodeStatus(ProcessNodeStatusConstant.END);
        curApprovalRecord.setApprover(approver);
        curApprovalRecord.setOperationType(handleProcessDto.getOperationType());
        curApprovalRecord.setApprovalOpinion(handleProcessDto.getApprovalOpinion());
        curApprovalRecord.setApprovalRemark(handleProcessDto.getApprovalRemark());
        curApprovalRecord.setUpdator(user.getId());
        processApprovalRecordMapper.updateById(curApprovalRecord);
        if (handleProcessDto.isEnd()) {
            // 手动完结
            return ProcessFlowStatusConstants.MANUAL_END;
        }
        // 获取下一个审批节点
        ProcessNodeDetail nextProcessNode = getNextProcessNode(serialNumber, processFlowId);
        if (Objects.isNull(nextProcessNode)) {
            // 自动完结
            return ProcessFlowStatusConstants.AUTO_END;
        }
        // 流程未完结，则生成下一个节点
        String appointApprover = nextProcessNode.getApprovers().stream().map(NodeApprover::getUserId).collect(Collectors.joining(","));
        CommonProcessApprovalRecord nextApprovalRecord = new CommonProcessApprovalRecord();
        nextApprovalRecord.setSerialNumber(serialNumber);
        nextApprovalRecord.setProcessFlowId(nextProcessNode.getId());
        nextApprovalRecord.setAppointApprover(appointApprover);
        nextApprovalRecord.setCreator(user.getId());
        nextApprovalRecord.setUpdator(user.getId());
        processApprovalRecordMapper.insert(nextApprovalRecord);
        return ProcessFlowStatusConstants.BEING;
    }

    /**
     * 多人会签
     *
     * @param handleProcessDto
     * @param curApprovalRecord
     * @return
     */
    private String andSignApprove(HandleProcessDto handleProcessDto, CommonProcessApprovalRecord curApprovalRecord) {
        CurrentUser user = contextUtil.getCurrentUser();
        String serialNumber = handleProcessDto.getSerialNumber();
        Integer processFlowId = handleProcessDto.getProcessFlowId();
        Integer approver = handleProcessDto.getApprover();
        List<String> appointApprovers = Arrays.asList(curApprovalRecord.getAppointApprover().split(","));
        if (!appointApprovers.contains(String.valueOf(approver))) {
            ServiceException.error("当前审批人不合法");
        }
        List<CommonProcessApprovalRecord> approvedRecords = processApprovalRecordMapper.getApprovedRecord(serialNumber, processFlowId);
        List<Integer> approvers = approvedRecords.stream().map(CommonProcessApprovalRecord::getApprover).collect(Collectors.toList());
        if (approvers.contains(approver)) {
            ServiceException.error("您已审批，请刷新");
        }
        // 处理节点
        curApprovalRecord.setNodeStatus(ProcessNodeStatusConstant.END);
        curApprovalRecord.setApprover(approver);
        curApprovalRecord.setOperationType(handleProcessDto.getOperationType());
        curApprovalRecord.setApprovalOpinion(handleProcessDto.getApprovalOpinion());
        curApprovalRecord.setApprovalRemark(handleProcessDto.getApprovalRemark());
        curApprovalRecord.setUpdator(user.getId());
        processApprovalRecordMapper.updateById(curApprovalRecord);
        if (handleProcessDto.isEnd()) {
            // 手动完结
            return ProcessFlowStatusConstants.MANUAL_END;
        }
        if (approvers.size() + 1 == appointApprovers.size()) {
            // 获取下一个审批节点
            ProcessNodeDetail nextProcessNode = getNextProcessNode(serialNumber, processFlowId);
            if (Objects.isNull(nextProcessNode)) {
                // 自动完结
                return ProcessFlowStatusConstants.AUTO_END;
            }
            // 流程未完结，则生成下一个节点
            String appointApprover = nextProcessNode.getApprovers().stream().map(NodeApprover::getUserId).collect(Collectors.joining(","));
            CommonProcessApprovalRecord nextApprovalRecord = new CommonProcessApprovalRecord();
            nextApprovalRecord.setSerialNumber(serialNumber);
            nextApprovalRecord.setProcessFlowId(nextProcessNode.getId());
            nextApprovalRecord.setAppointApprover(appointApprover);
            nextApprovalRecord.setCreator(user.getId());
            nextApprovalRecord.setUpdator(user.getId());
            processApprovalRecordMapper.insert(nextApprovalRecord);
            return ProcessFlowStatusConstants.BEING;
        } else {
            // 该节点还有人未审批，则生成新的一条审批记录
            CommonProcessApprovalRecord sameApprovalRecord = new CommonProcessApprovalRecord();
            sameApprovalRecord.setSerialNumber(serialNumber);
            sameApprovalRecord.setProcessFlowId(processFlowId);
            sameApprovalRecord.setAppointApprover(curApprovalRecord.getAppointApprover());
            sameApprovalRecord.setCreator(user.getId());
            sameApprovalRecord.setUpdator(user.getId());
            processApprovalRecordMapper.insert(sameApprovalRecord);
            return ProcessFlowStatusConstants.BEING;
        }
    }

}
